<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */

namespace common\components;

use yii\redis\Cache as BaseCache;
use yii\helpers\StringHelper;
use yii\caching\Dependency;
/**
 *
 * @author nancy xu 
 * @since 2.0
 */
class Cache extends BaseCache
{
    /**
     * @var 缓存的子前缀，用完里面设置为空
     */
    protected $subPrefix = null;

    /**
     * 设置子前缀
     */
    public function setSubKey($value = null)
    {
        $this->subPrefix = $value;
    }

    /**
     * Builds a normalized cache key from a given key.
     *
     * If the given key is a string containing alphanumeric characters only and no more than 32 characters,
     * then the key will be returned back prefixed with [[keyPrefix]]. Otherwise, a normalized key
     * is generated by serializing the given key, applying MD5 hashing, and prefixing with [[keyPrefix]].
     *
     * @param mixed $key the key to be normalized
     * @param string $subPrefix: 子前缀
     * @return string the generated cache key
     */
    public function buildPrefixKey($key, $subPrefix = null)
    {
        if ($subPrefix) {
            $this->setSubKey($subPrefix);
        }
        if (is_string($key)) {
            $key = ctype_alnum($key) && StringHelper::byteLength($key) <= 32 ? $key : md5($key);
        } else {
            $key = md5(json_encode($key));
        }

        return $this->keyPrefix . $this->subPrefix. $key;
    }

    /**
     * 有子前缀的获取
     * @param mixed $key a key identifying the cached value. This can be a simple string or
     * a complex data structure consisting of factors representing the key.
     * @param string $subPrefix: 子前缀
     * @return mixed the value stored in cache, false if the value is not in the cache, expired,
     * or the dependency associated with the cached data has changed.
     */
    public function prefixGet($key, $subPrefix = null)
    {   
        $key = $this->buildPrefixKey($key, $subPrefix);
        $value = $this->getValue($key);
        if ($value === false || $this->serializer === false) {
            return $value;
        } elseif ($this->serializer === null) {
            $value = unserialize($value);
        } else {
            $value = call_user_func($this->serializer[1], $value);
        }
        if (is_array($value) && !($value[1] instanceof Dependency && $value[1]->isChanged($this))) {
            return $value[0];
        }

        return false;
    }

    /**
     * Stores a value identified by a key into cache.
     * If the cache already contains such a key, the existing value and
     * expiration time will be replaced with the new ones, respectively.
     *
     * @param mixed $key a key identifying the value to be cached. This can be a simple string or
     * a complex data structure consisting of factors representing the key.
     * @param mixed $value the value to be cached
     * @param string $subPrefix: 子前缀
     * @param int $duration default duration in seconds before the cache will expire. If not set,
     * default [[defaultDuration]] value is used.
     * @param Dependency $dependency dependency of the cached item. If the dependency changes,
     * the corresponding value in the cache will be invalidated when it is fetched via [[get()]].
     * This parameter is ignored if [[serializer]] is false.
     * @return bool whether the value is successfully stored into cache
     */
    public function prefixSet($key, $value, $subPrefix = null, $duration = null, $dependency = null)
    {
        if ($duration === null) {
            $duration = $this->defaultDuration;
        }

        if ($dependency !== null && $this->serializer !== false) {
            $dependency->evaluateDependency($this);
        }
        if ($this->serializer === null) {
            $value = serialize([$value, $dependency]);
        } elseif ($this->serializer !== false) {
            $value = call_user_func($this->serializer[0], [$value, $dependency]);
        }
        $key = $this->buildPrefixKey($key, $subPrefix);

        return $this->setValue($key, $value, $duration);
    }
    /**
     * Checks whether a specified key exists in the cache.
     * This can be faster than getting the value from the cache if the data is big.
     * In case a cache does not support this feature natively, this method will try to simulate it
     * but has no performance improvement over getting it.
     * Note that this method does not check whether the dependency associated
     * with the cached data, if there is any, has changed. So a call to [[get]]
     * may return false while exists returns true.
     * @param mixed $key a key identifying the cached value. This can be a simple string or
     * a complex data structure consisting of factors representing the key.
     * @param string $subPrefix: 子前缀
     * @return bool true if a value exists in cache, false if the value is not in the cache or expired.
     */
    public function prefixExists($key, $subPrefix = null)
    {
        $key = $this->buildPrefixKey($key, $subPrefix);
        $value = $this->getValue($key);

        return $value !== false;
    }


    /**
     * 删除有子前缀的缓存
     * @param mixed $key a key identifying the value to be deleted from cache. This can be a simple string or
     * a complex data structure consisting of factors representing the key.
     * @return bool if no error happens during deletion
     */
    public function prefixDelete($key, $subPrefix = null)
    {
        $key = $this->buildPrefixKey($key, $subPrefix);

        return $this->deleteValue($key);
    }
}
